package org.jeecg.modules.tankerRegistration.scheduled;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;
import org.jeecg.common.util.DateUtils;
import org.jeecg.modules.backups.entity.Backups;
import org.jeecg.modules.backups.service.IBackupsService;
import org.jeecg.modules.tankerRegistration.entity.TankerRegistration;
import org.jeecg.modules.tankerRegistration.service.ITankerRegistrationService;
import org.jeecg.modules.tankerRules.entity.TankerRules;
import org.jeecg.modules.tankerRules.service.ITankerRulesService;
import org.jeecgframework.poi.excel.ExcelExportUtil;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Configuration
@EnableScheduling
@Slf4j
public class MyScheduled {

    private final ITankerRegistrationService tankerRegistrationService;
    private final ITankerRulesService tankerRulesService;
    private final IBackupsService backupsService;

    @Autowired
    public MyScheduled(ITankerRegistrationService tankerRegistrationService, ITankerRulesService tankerRulesService, IBackupsService backupsService) {
        this.tankerRegistrationService = tankerRegistrationService;
        this.tankerRulesService = tankerRulesService;
        this.backupsService = backupsService;
    }

    @Scheduled(cron = "0 0 7-19 * * ?")//早七晚七的每小时执行一次
    public void configureTasks() throws Exception{
        TankerRules rules = tankerRulesService.getRules();
        LambdaQueryWrapper<TankerRegistration> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(TankerRegistration::getEntryStatus, 1);
        wrapper.in(TankerRegistration::getAuditStatus,1, 2);
        List<TankerRegistration> tankerRegistrations = tankerRegistrationService.list(wrapper);
        List<TankerRegistration> updatedTankerRegistrations = new ArrayList<>();
        for (TankerRegistration tankerRegistration : tankerRegistrations) {
            if (rules.getRegister()){
                Integer registerTime = rules.getRegisterTime();
                Date registerDate = tankerRegistration.getCreateTime();//登记时间
                if (isExpire(registerDate, registerTime)) {//是否过期
                    tankerRegistration.setAuditStatus(15);
                    updatedTankerRegistrations.add(tankerRegistration);
                }
            }
            if (rules.getAudit() && tankerRegistration.getAuditTime1() != null){//三载通过时间
                Integer auditTime = rules.getAuditTime();
                Date auditDate = tankerRegistration.getAuditTime1();
                if (isExpire(auditDate, auditTime)) {//是否过期
                    tankerRegistration.setAuditStatus(15);
                    updatedTankerRegistrations.add(tankerRegistration);
                }
            }
        }
        if (!updatedTankerRegistrations.isEmpty()){
            tankerRegistrationService.updateBatchById(updatedTankerRegistrations);
        }
    }

    //是否过期
    private static boolean isExpire(Date registerDate, Integer registerTime) {
        // 将Date转换为LocalDateTime
        LocalDateTime registerLocalDateTime = registerDate.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDateTime();
        // 当前时间转换为LocalDateTime
        LocalDateTime currentTime = LocalDateTime.now();
        // 将registerDate加上registerTime小时
        LocalDateTime expirationTime = registerLocalDateTime.plusHours(registerTime);
        return expirationTime.isBefore(currentTime);
    }

    @Scheduled(cron = "0 30 2 * * ?")
    public void backups(){
        log.info("---------------开始执行备份----------------");
        String backupsDir = "D:\\zcl_backups\\poi";
        Calendar calendar = Calendar.getInstance();// 获取当前时间
        calendar.add(Calendar.DAY_OF_MONTH, -1);// 将时间减去一天
        String s = DateUtils.formatDate(calendar);
        String filename = s + ".xlsx";
        String zipName = s + "zip.zip";
        List<TankerRegistration> tankerRegistrations = tankerRegistrationService.getBackups(s);
        List<TankerRegistration> picList = new ArrayList<>(tankerRegistrations);
        try {
            Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(
                    s+"备份", "备份"), TankerRegistration.class, tankerRegistrations);
            File savefile = new File(backupsDir);
            if (!savefile.exists()) {
                boolean mkDirs = savefile.mkdirs();
                if (!mkDirs){
                    throw new RuntimeException("创建文件夹失败");
                }
            }
            FileOutputStream fos = new FileOutputStream(backupsDir + "\\" + filename);
            workbook.write(fos);
            fos.close();

            // 获取文件大小并转换为KB
            Path filePath = Paths.get(backupsDir, filename);
            long fileSizeBytes = Files.size(filePath);
            int fileSizeKB = (int) (fileSizeBytes / 1024);

            // 创建临时目录用于存储各个TankerRegistration的zip文件
            Path tempZipDir = Files.createTempDirectory("tankerRegistration_zips");

            try {
                // 获取所有标记为图片的字段
                List<Field> imageFields = getImageFields(TankerRegistration.class);

                // 为每个TankerRegistration创建独立的zip文件
                for (TankerRegistration backup : picList) {
                    createZipForTankerRegistration(backup, imageFields, tempZipDir);
                }

                // 将所有独立的zip文件打包成一个总的zip文件
                Path finalZipPath = Paths.get(backupsDir, zipName);
                try (ZipOutputStream mainZos = new ZipOutputStream(Files.newOutputStream(finalZipPath))) {
                    File[] zipFiles = tempZipDir.toFile().listFiles((dir, name) -> name.endsWith(".zip"));
                    if (zipFiles != null) {
                        for (File zipFile : zipFiles) {
                            addFileToZip(mainZos, zipFile, zipFile.getName());
                        }
                    }
                }

                // 获取总zip文件大小并转换为KB
                long zipSizeBytes = Files.size(finalZipPath);
                int zipSizeKB = (int) (zipSizeBytes / 1024);

                Backups backups = new Backups();
                backups.setFileName(filename).setFileUrl(filename).setFileSize(fileSizeKB)
                        .setZipName(zipName).setZipUrl(zipName).setZipSize(zipSizeKB)
                        .setBackupsTime(new Date()).setStatus(1);
                backupsService.save(backups);
                // 清理超过一个月的文件
                cleanOldFiles(savefile);
            } finally {
                // 清理临时目录
                deleteDirectory(tempZipDir.toFile());
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 为单个TankerRegistration对象创建包含其所有图片的zip文件
     *
     * @param registration TankerRegistration对象
     * @param imageFields 标记为图片的字段列表
     * @param outputDir 输出zip文件的目录
     * @throws IOException IO异常
     * @throws IllegalAccessException 反射访问异常
     */
    public static void createZipForTankerRegistration(TankerRegistration registration,
                                                       List<Field> imageFields,
                                                       Path outputDir) throws IOException, IllegalAccessException {
        // 收集当前TankerRegistration对象的所有图片URL
        List<String> urls = new ArrayList<>();

        // 遍历所有图片字段并提取URL
        for (Field field : imageFields) {
            try {
                field.setAccessible(true);
                Object value = field.get(registration);
                if (value instanceof String) {
                    List<String> fieldUrls = extractUrls((String) value);
                    urls.addAll(fieldUrls);
                }
            } catch (IllegalAccessException e) {
                log.error("无法访问字段: " + field.getName(), e);
                throw e; // 重新抛出异常
            }
        }

        // 如果没有图片则直接返回
        if (urls.isEmpty()) {
            log.info("TankerRegistration {} 没有图片需要处理", registration.getId());
            return;
        }

        // 使用TankerRegistration的id作为zip文件名
        String name = registration.getPlateNo() + "_" + DateUtils.formatDate(registration.getCreateTime(), "yyyyMMddHHmmss");
        String subZipName = name + ".zip";
        Path subZipPath = outputDir.resolve(subZipName);

        // 创建临时目录用于下载图片
        Path tempDir = Files.createTempDirectory("img_temp_" + name);

        try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(subZipPath))) {

            // 下载并添加每个图片到zip文件中
            for (String imageUrl : urls) {
                try {
                    File tempImageFile = downloadImage(imageUrl, tempDir);
                    addFileToZip(zos, tempImageFile, tempImageFile.getName());
                    Files.deleteIfExists(tempImageFile.toPath());
                } catch (IOException e) {
                    log.error("下载图片失败: " + imageUrl, e);
                    throw e; // 重新抛出异常
                }
            }
        } finally {
            // 清理临时图片目录
            deleteDirectory(tempDir.toFile());
        }
    }


    /**
     * 获取类中所有标记为图片的字段（@Excel(type = 2)）
     * @param clazz 类
     * @return 图片字段列表
     */
    public static List<Field> getImageFields(Class<?> clazz) {
        List<Field> imageFields = new ArrayList<>();
        Field[] fields = clazz.getDeclaredFields();

        for (Field field : fields) {
            Excel excelAnnotation = field.getAnnotation(Excel.class);
            if (excelAnnotation != null && excelAnnotation.type() == 2) {
                imageFields.add(field);
            }
        }

        return imageFields;
    }

    /**
     * 清理超过一个月的文件
     * @param directory  目录
     */
    private void cleanOldFiles(File directory) {
        if (directory == null || !directory.exists() || !directory.isDirectory()) {
            log.warn("提供的路径不是有效的目录: {}", directory);
            return;
        }
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime cutoffTime = now.minusDays(30); // 计算30天前的时间点
        File[] files = directory.listFiles();
        if (files != null) {
            List<Backups> backupsList = new ArrayList<>();
            for (File file : files) {
                LocalDateTime lastModifiedTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(file.lastModified()), ZoneId.systemDefault());
                if (lastModifiedTime.isBefore(cutoffTime)) {
                    if (!file.delete()) {
                        log.warn("无法删除文件: {}", file.getAbsolutePath());
                    } else {
                        log.info("已成功删除过期文件: {}", file.getAbsolutePath());
                        // 更新数据库中的记录状态
                        String fileName = file.getName();
                        log.info("将要删除的文件名: {}", fileName);
                        Backups byFileName = backupsService.getByFileName(fileName);
                        if (byFileName != null) {
                            byFileName.setStatus(0);
                            backupsList.add(byFileName);
                            log.info("已将文件 {} 的状态更新为 0", fileName);
                        }
                    }
                }
            }
            backupsService.updateBatchById(backupsList);
        } else {
            log.warn("没有找到任何文件需要清理.");
        }
    }

    /**
     * 压缩图片并保存为ZIP文件
     * @param imageUrlList 图片URL列表
     * @param outputPath 输出ZIP文件的路径
     * @throws IOException
     */
    public static void compressImagesToZip(List<String> imageUrlList, String outputPath) throws IOException {
        // Create a temporary directory to store images before compression.
        Path tempDir = Files.createTempDirectory("img_temp_");
        try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(Paths.get(outputPath)))) {
            for (String imageUrl : imageUrlList) {
                // Download image and save it to the temporary directory.
                File tempImageFile = downloadImage(imageUrl, tempDir);

                // Add the file to the zip output stream.
                addFileToZip(zos, tempImageFile, tempImageFile.getName());

                // Delete the image file after adding to zip.
                Files.deleteIfExists(tempImageFile.toPath());
            }
        } finally {
            // Clean up the temporary directory.
            deleteDirectory(tempDir.toFile());
        }
    }

    /**
     * 下载图片并保存为临时文件
     * @param imageUrl 图片URL
     * @param dir 保存图片的目录
     * @return 保存图片的临时文件
     * @throws IOException 抛出的异常
     */
    public static File downloadImage(String imageUrl, Path dir) throws IOException {
        URL url = new URL(imageUrl);
        InputStream in = url.openStream();
        Path tempFilePath = Files.createTempFile(dir, "image", getExtension(imageUrl));
        Files.copy(in, tempFilePath, StandardCopyOption.REPLACE_EXISTING);
        in.close();
        return tempFilePath.toFile();
    }

    /**
     * 添加文件到ZIP输出流中
     * @param zos ZIP输出流
     * @param file 要添加的文件
     * @param fileName 文件名
     * @throws IOException 抛出的异常
     */
    public static void addFileToZip(ZipOutputStream zos, File file, String fileName) throws IOException {
        FileInputStream fis = new FileInputStream(file);
        ZipEntry zipEntry = new ZipEntry(fileName);
        zos.putNextEntry(zipEntry);

        byte[] bytes = new byte[1024];
        int length;
        while ((length = fis.read(bytes)) >= 0) {
            zos.write(bytes, 0, length);
        }

        zos.closeEntry();
        fis.close();
    }

    /**
     * 获取图片的扩展名
     * @param imageUrl 图片URL
     * @return 图片的扩展名
     */
    public static String getExtension(String imageUrl) {
        if (imageUrl == null || imageUrl.isEmpty()) return "";
        int dotPos = imageUrl.lastIndexOf('.');
        if (dotPos == -1) return "";
        return imageUrl.substring(dotPos);
    }

    /**
     * 删除目录及其下的所有文件
     * @param directory 要删除的目录
     */
    public static void deleteDirectory(File directory) {
        if (directory.isDirectory()) {
            File[] entries = directory.listFiles();
            if (entries != null) {
                for (File entry : entries) {
                    deleteDirectory(entry);
                }
            }
        }
        directory.delete();
    }

    /**
     * 获取图片的URL列表
     * @param input 输入字符串
     * @return 图片的URL列表
     */
    public static List<String> extractUrls(String input) {
        if (input == null || input.trim().isEmpty()) {
            return Collections.emptyList(); // 或者根据需求返回其他默认值
        }
        return Arrays.stream(input.split(","))
                .map(String::trim) // 去除每个URL前后的空格
                .filter(url -> !url.isEmpty()) // 排除空字符串
                .map(url -> url.startsWith("http") ? url : "https://zcl-tanker.obs.cn-north-4.myhuaweicloud.com/20" + url)
                .collect(Collectors.toList());
    }

}
